//
// Created by entaoyang@163.com on 2017/10/10.
// Copyright (c) 2017 yet.net. All rights reserved.
//

import Foundation


public extension Double {
    var afterNow: DispatchTime {
        DispatchTime.now() + self
    }
}

public extension Int {
    var afterNow: DispatchTime {
        DispatchTime.now() + Double(self)
    }
    var timeInterval: TimeInterval {
        TimeInterval(self)
    }
}

public extension NSObject {
    func enterSync() {
        objc_sync_enter(self)
    }

    func exitSync() {
        objc_sync_exit(self)
    }

    func syncInvoke(_ block: BlockVoid) {
        objc_sync_enter(self)
        defer {
            objc_sync_exit(self)
        }
        block()
    }

}


public func mainInvoke(_ c: @escaping BlockVoid) {
    if Thread.current.isMainThread {
        c()
    } else {
        Task.fore {
            c()
        }
    }
}

public class TaskJoin: NSObject {
    private var callback: BlockVoid? = nil
    private let total: Int
    private var current: Int = 0 {
        didSet {
            if current == total {
                fire()
            }
        }
    }
    public var mainThread: Bool = true

    public var timeout: TimeInterval = 10


    public init(_ total: Int, _ block: @escaping BlockVoid) {
        self.total = total
        self.callback = block
    }

    public func fire() {
        if let c = callback {
            callback = nil
            if mainThread {
                mainInvoke(c)
            } else {
                c()
            }
        }
    }


    public func inc() {
        self.syncInvoke {
            current += 1
        }
    }

}

public class Task {
    fileprivate static var onceSet: Set<String> = Set<String>()

    public static func runOnce(_ key: String, _ block: () -> Void) {
        if !onceSet.contains(key) {
            onceSet.insert(key)
            block()
        }
    }
}

fileprivate func foreWork(_ block: @escaping BlockVoid) -> DispatchWorkItem {
    DispatchWorkItem(qos: .userInitiated, flags: [.barrier, .assignCurrentContext], block: block)
}

fileprivate func backWork(_ block: @escaping BlockVoid) -> DispatchWorkItem {
    DispatchWorkItem(qos: .utility, flags: [.barrier, .assignCurrentContext], block: block)
}

public extension Task {

    @discardableResult
    static func foreX(_ block: @escaping BlockVoid) -> DispatchWorkItem {
        let item = foreWork(block)
        DispatchQueue.main.async(execute: item)
        return item
    }

    @discardableResult
    static func foreDelayX(seconds: Double, _ block: @escaping BlockVoid) -> DispatchWorkItem {
        let item = foreWork(block)
        DispatchQueue.main.asyncAfter(deadline: seconds.afterNow, execute: item)
        return item
    }

    static func fore(_ block: @escaping BlockVoid) {
        DispatchQueue.main.async(execute: foreWork(block))
    }

    static func foreDelay(seconds: Double, _ block: @escaping BlockVoid) {
        DispatchQueue.main.asyncAfter(deadline: seconds.afterNow, execute: foreWork(block))
    }

    static func back(_ block: @escaping BlockVoid) {
        DispatchQueue.global(qos: .utility).async(execute: backWork(block))
    }

    static func backDelay(seconds: Double, _ block: @escaping BlockVoid) {
        DispatchQueue.global(qos: .utility).asyncAfter(deadline: seconds.afterNow, execute: backWork(block))
    }


}

public class TaskQueue: NSObject {
    public let queue: DispatchQueue

    public init(_ label: String) {
        self.queue = DispatchQueue(label: label, qos: .utility)
        super.init()
    }

    public func sync(_ block: @escaping BlockVoid) {
        self.queue.sync(execute: block)
    }

    public func fore(_ block: @escaping BlockVoid) {
        Task.fore(block)
    }

    public func foreDelay(seconds: Double, _ block: @escaping BlockVoid) {
        Task.foreDelay(seconds: seconds, block)
    }


    public func backDelay(seconds: Double, _ block: @escaping BlockVoid) {
        self.queue.asyncAfter(deadline: seconds.afterNow, execute: backWork(block))
    }

    public func back(_ block: @escaping BlockVoid) {
        self.queue.async(execute: backWork(block))
    }
}



